/**
 * 
 */
package org.swing.utility.common.file;

import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileAlreadyExistsException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.zip.GZIPInputStream;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.swing.utility.common.datetime.DateTimeUtil;
import org.swing.utility.common.util.CommonUtil;
import org.swing.utility.system.flatform.SystemUtil;

/**
 * @author lqnhu
 *
 */
public class FileUtil {

	private static boolean equals(InputStream is1, InputStream is2)
			throws IOException {
		int BUFFSIZE = 1024;
		byte buf1[] = new byte[BUFFSIZE];
		byte buf2[] = new byte[BUFFSIZE];
		if (is1 == is2) {
			return true;
		}
		if (is1 == null && is2 == null) {
			return true;
		}
		if (is1 == null || is2 == null) {
			return false;
		}
		int read1 = -1;
		int read2 = -1;
		do {
			int offset1 = 0;
			while (offset1 < BUFFSIZE
					&& (read1 = is1.read(buf1, offset1, BUFFSIZE - offset1)) >= 0) {
				offset1 += read1;
			}
			int offset2 = 0;
			while (offset2 < BUFFSIZE
					&& (read2 = is2.read(buf2, offset2, BUFFSIZE - offset2)) >= 0) {
				offset2 += read2;
			}
			if (offset1 != offset2) {
				return false;
			}
			if (offset1 != BUFFSIZE) {
				Arrays.fill(buf1, offset1, BUFFSIZE, (byte) 0);
				Arrays.fill(buf2, offset2, BUFFSIZE, (byte) 0);
			}
			if (!Arrays.equals(buf1, buf2)) {
				return false;
			}
		} while (read1 >= 0 && read2 >= 0);
		if (read1 < 0 && read2 < 0) {
			return true; // both at EOF
		}
		return false;
	}

	/**
	 * Tests whether the contents of two files equals each other by performing a
	 * byte-by-byte comparison. Each byte must match each other in both files.
	 * 
	 * @param file1
	 *            The file to compare
	 * @param file2
	 *            The other file to compare
	 * @return True if file contents are equal, otherwise false.
	 * @throws IOException
	 *             Thrown if there is an underlying IO error while attempt to
	 *             compare the bytes.
	 */
	public static boolean equals(File file1, File file2) throws IOException {
		// file lengths must match
		if (file1.length() != file2.length()) {
			return false;
		}
		InputStream is1 = null;
		InputStream is2 = null;
		try {
			is1 = new FileInputStream(file1);
			is2 = new FileInputStream(file2);
			return equals(is1, is2);
		} finally {
			// make sure input streams are closed
			if (is1 != null) {
				try {
					is1.close();
				} catch (Exception e) {
				}
			}
			if (is2 != null) {
				try {
					is2.close();
				} catch (Exception e) {
				}
			}
		}
	}

	/**
	 * Checks if the extension is valid. This method only permits letters,
	 * digits, and an underscore character.
	 * 
	 * @param extension
	 *            The file extension to validate
	 * @return True if its valid, otherwise false
	 */
	public static boolean isValidFileExtension(String extension) {
		for (int i = 0; i < extension.length(); i++) {
			char c = extension.charAt(i);
			if (!(Character.isDigit(c) || Character.isLetter(c) || c == '_')) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Parse the filename and return the file extension. For example, if the
	 * file is "app.2006-10-10.log", then this method will return "log". Will
	 * only return the last file extension. For example, if the filename ends
	 * with ".log.gz", then this method will return "gz"
	 * 
	 * @param String
	 *            to process containing the filename
	 * @return The file extension (without leading period) such as "gz" or "txt"
	 *         or null if none exists.
	 */
	public static String parseFileExtension(String filename) {
		// if null, return null
		if (filename == null) {
			return null;
		}
		// find position of last period
		int pos = filename.lastIndexOf('.');
		// did one exist or have any length?
		if (pos < 0 || (pos + 1) >= filename.length()) {
			return null;
		}
		// parse extension
		return filename.substring(pos + 1);
	}

	/**
	 * Finds all files (non-recursively) in a directory based on the FileFilter.
	 * This method is slightly different than the JDK File.listFiles() version
	 * since it throws an error if the directory does not exist or is not a
	 * directory. Also, this method only finds files and will skip including
	 * directories.
	 */
	public static File[] findFiles(File dir, FileFilter filter)
			throws FileNotFoundException {
		if (!dir.exists()) {
			throw new FileNotFoundException("Directory " + dir
					+ " does not exist.");
		}
		if (!dir.isDirectory()) {
			throw new FileNotFoundException("File " + dir
					+ " is not a directory.");
		}
		// being matching process, create array for returning results
		ArrayList<File> files = new ArrayList<File>();
		// get all files in this directory
		File[] allFiles = dir.listFiles();
		// were any files returned?
		if (allFiles != null && allFiles.length > 0) {
			// loop thru every file in the dir
			for (File f : allFiles) {
				// only match files, not a directory
				if (f.isFile()) {
					// delegate matching to provided file matcher
					if (filter.accept(f)) {
						files.add(f);
					}
				}
			}
		}
		// based on filesystem, order of files not guaranteed -- sort now
		File[] r = files.toArray(new File[0]);
		Arrays.sort(r);
		return r;
	}

	/**
	 * Get all of the files (not dirs) under <CODE>dir</CODE>
	 * 
	 * @param dir
	 *            Directory to search.
	 * @return all the files under <CODE>dir</CODE>
	 */
	/**
	 * public static Set<File> getRecursiveFiles(File dir) throws IOException {
	 * if (!dir.isDirectory()) { HashSet<File> one = new HashSet<File>();
	 * one.add(dir); return one; } else { Set<File> ret = recurseDir(dir);
	 * return ret; } } private static Set<File> recurseDir(File dir) throws
	 * IOException { HashSet<File> c = new HashSet<File>(); File[] files =
	 * dir.listFiles(); for (int i = 0; i < files.length; i++) { if
	 * (files[i].isDirectory()) { c.addAll(recurseDir(files[i])); } else {
	 * c.add(files[i]); } } return c; }
	 */
	/**
	 * public static boolean rmdir(File dir, boolean recursive) throws
	 * IOException { // make sure this is a directory if (!dir.isDirectory()) {
	 * throw new IOException("File " + dir + " is not a directory"); } File[]
	 * files = dir.listFiles(); // are there files? if (files != null &&
	 * files.length > 0 && !recursive) { throw new IOException("Directory " +
	 * dir + " is not empty, cannot be deleted"); } for (File file : files) { if
	 * (file.isDirectory()) { rmdir(file); } else { file.delete(); } } //
	 * finally remove this directory return dir.delete(); } private static
	 * boolean rmdir(File dir) throws IOException { // make sure this is a
	 * directory if (!dir.isDirectory()) { throw new IOException("File " + dir +
	 * " is not a directory"); } File[] files = dir.listFiles(); // are there
	 * files? if (files != null && files.length > 0 && !recursive) { throw new
	 * IOException("Directory " + dir + " is not empty, cannot be deleted"); }
	 * boolean success = true; for (File file : files) { if (file.isDirectory())
	 * { success |= rmdir(file); } else { success |= file.delete(); } } // was
	 * the recursive part okay? // finally remove this directory return
	 * dir.delete(); }
	 */
	/**
	 * Copy dest.length bytes from the inputstream into the dest bytearray.
	 * 
	 * @param is
	 * @param dest
	 * @throws IOException
	 */
	/**
	 * public static void copy(InputStream is, byte[] dest) throws IOException {
	 * int len = dest.length; int ofs = 0; while (len > 0) { int size =
	 * is.read(dest, ofs, len); ofs += size; len -= size; } }
	 */
	/**
	 * Copy the source file to the target file.
	 * 
	 * @param sourceFile
	 *            The source file to copy from
	 * @param targetFile
	 *            The target file to copy to
	 * @throws FileAlreadyExistsException
	 *             Thrown if the target file already exists. This exception is a
	 *             subclass of IOException, so catching an IOException is enough
	 *             if you don't care about this specific reason.
	 * @throws IOException
	 *             Thrown if an error during the copy
	 */
	public static void copy(File sourceFile, File targetFile)
			throws FileAlreadyExistsException, IOException {
		copy(sourceFile, targetFile, false);
	}

	/**
	 * Copy the source file to the target file while optionally permitting an
	 * overwrite to occur in case the target file already exists.
	 * 
	 * @param sourceFile
	 *            The source file to copy from
	 * @param targetFile
	 *            The target file to copy to
	 * @return True if an overwrite occurred, otherwise false.
	 * @throws FileAlreadyExistsException
	 *             Thrown if the target file already exists and an overwrite is
	 *             not permitted. This exception is a subclass of IOException,
	 *             so catching an IOException is enough if you don't care about
	 *             this specific reason.
	 * @throws IOException
	 *             Thrown if an error during the copy
	 */
	public static boolean copy(File sourceFile, File targetFile,
			boolean overwrite) throws FileAlreadyExistsException, IOException {
		boolean overwriteOccurred = false;
		// check if the targetFile already exists
		if (targetFile.exists()) {
			// if overwrite is not allowed, throw an exception
			if (!overwrite) {
				throw new FileAlreadyExistsException("Target file "
						+ targetFile + " already exists");
			} else {
				// set the flag that it occurred
				overwriteOccurred = true;
			}
		}
		// proceed with copy
		FileInputStream fis = new FileInputStream(sourceFile);
		FileOutputStream fos = new FileOutputStream(targetFile);
		fis.getChannel().transferTo(0, sourceFile.length(), fos.getChannel());
		fis.close();
		fos.flush();
		fos.close();
		return overwriteOccurred;
	}

	/**
	 * Return a File handle to a temporary file location
	 *
	 * @param ext
	 *            the suffix of the filename
	 * @param deleteOnExit
	 *            whether to delete this file after the JVM exits
	 * @return
	 */
	public static File getTempFile(String ext, boolean deleteOnExit) {
		return getTempFile((String) null, ext, deleteOnExit);
	}

	public static File getTempFile(String ext) {
		return (FileUtil.getTempFile((String) null, ext, false));
	}

	public static File getTempFile(String prefix, String suffix,
			boolean deleteOnExit) {
		return getTempFile(new File(System.getProperty("java.io.tmpdir")),
				prefix, suffix, deleteOnExit);
	}

	public static File getTempFile(File tempDir, String ext,
			boolean deleteOnExit) {
		return getTempFile(tempDir, null, ext, deleteOnExit);
	}

	public static File getTempFile(File tempDir, String prefix, String suffix,
			boolean deleteOnExit) {
		File tempFile;
		if (suffix != null && suffix.startsWith(".") == false)
			suffix = "." + suffix;
		if (prefix == null)
			prefix = "hstore";
		try {
			tempFile = File.createTempFile(prefix, suffix, tempDir);
			if (deleteOnExit)
				tempFile.deleteOnExit();
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
		return (tempFile);
	}

	/**
	 * Unsafely create a temporary directory Yes I said that this was unsafe. I
	 * don't care...
	 * 
	 * @return
	 */
	public static File getTempDirectory() {
		return getTempDirectory(null);
	}

	public static File getTempDirectory(String suffix) {
		final File temp = FileUtil.getTempFile(suffix);
		if (!(temp.delete())) {
			throw new RuntimeException("Could not delete temp file: "
					+ temp.getAbsolutePath());
		} else if (!(temp.mkdir())) {
			throw new RuntimeException("Could not create temp directory: "
					+ temp.getAbsolutePath());
		}
		return (temp);
	}

	public static File writeStringToFile(String file_path, String content)
			throws IOException {
		return (FileUtil.writeStringToFile(new File(file_path), content));
	}

	public static File writeStringToFile(File file, String content)
			throws IOException {
		FileWriter writer = new FileWriter(file);
		writer.write(content);
		writer.flush();
		writer.close();
		return (file);
	}

	/**
	 * Write the given string to a temporary file Will not delete the file after
	 * the JVM exits
	 *
	 * @param content
	 * @return
	 */
	public static File writeStringToTempFile(String content) {
		return (writeStringToTempFile(content, "tmp", false));
	}

	/**
	 * Write the given string to a temporary file with the given extension as
	 * the suffix Will not delete the file after the JVM exits
	 *
	 * @param content
	 * @param ext
	 * @return
	 */
	public static File writeStringToTempFile(String content, String ext) {
		return (writeStringToTempFile(content, ext, false));
	}

	/**
	 * Write the given string to a temporary file with the given extension as
	 * the suffix If deleteOnExit is true, then the file will be removed when
	 * the JVM exits
	 *
	 * @param content
	 * @param ext
	 * @param deleteOnExit
	 * @return
	 */
	public static File writeStringToTempFile(String content, String ext,
			boolean deleteOnExit) {
		File tempFile = FileUtil.getTempFile(ext, deleteOnExit);
		try {
			FileUtil.writeStringToFile(tempFile, content);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return tempFile;
	}

	public static String readFile(File path) {
		return (readFile(path.getAbsolutePath()));
	}

	public static String readFile(String path) {
		StringBuilder buffer = new StringBuilder();
		try {
			BufferedReader in = FileUtil.getReader(path);
			while (in.ready()) {
				buffer.append(in.readLine()).append("\n");
			} // WHILE
			in.close();
		} catch (IOException ex) {
			throw new RuntimeException("Failed to read file contents from '"
					+ path + "'", ex);
		}
		return (buffer.toString());
	}

	/**
	 * Creates a BufferedReader for the given input path Can handle both gzip
	 * and plain text files
	 *
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static BufferedReader getReader(String path) throws IOException {
		return (FileUtil.getReader(new File(path)));
	}

	/**
	 * Creates a BufferedReader for the given input path Can handle both gzip
	 * and plain text files
	 *
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static BufferedReader getReader(File file) throws IOException {
		if (!file.exists()) {
			throw new IOException("The file '" + file + "' does not exist");
		}
		BufferedReader in = null;
		if (file.getPath().endsWith(".gz")) {
			FileInputStream fin = new FileInputStream(file);
			GZIPInputStream gzis = new GZIPInputStream(fin);
			in = new BufferedReader(new InputStreamReader(gzis));
		} else {
			in = new BufferedReader(new FileReader(file));
		}
		return (in);
	}

	public static byte[] readBytesFromFile(String path) throws IOException {
		File file = new File(path);
		FileInputStream in = new FileInputStream(file);
		// Create the byte array to hold the data
		long length = file.length();
		byte[] bytes = new byte[(int) length];
		// Read in the bytes
		int offset = 0;
		int numRead = 0;
		while ((offset < bytes.length)
				&& ((numRead = in.read(bytes, offset, bytes.length - offset)) >= 0)) {
			offset += numRead;
		} // WHILE
		if (offset < bytes.length) {
			throw new IOException("Failed to read the entire contents of '"
					+ file.getName() + "'");
		}
		in.close();
		return (bytes);
	}

	/**
	 * Recursively delete all of the contents of the given directory
	 * 
	 * @param path
	 * @return
	 */
	public static boolean deleteDirectory(File path) {
		if (path.exists()) {
			File[] files = path.listFiles();
			for (File f : files) {
				if (f.isDirectory()) {
					deleteDirectory(f);
				} else {
					f.delete();
				}
			}
		}
		return path.delete();
	}

	/**
	 * Find the path to a directory below our current location in the source
	 * tree Throws a RuntimeException if we go beyond our repository checkout
	 *
	 * @param dirName
	 * @return
	 * @throws IOException
	 */
	public static File findDirectory(String dirName) throws IOException {
		return (FileUtil.find(dirName, new File(".").getCanonicalFile(), true)
				.getCanonicalFile());
	}

	/**
	 * Find the path to a directory below our current location in the source
	 * tree Throws a RuntimeException if we go beyond our repository checkout
	 *
	 * @param dirName
	 * @return
	 * @throws IOException
	 */
	public static File findFile(String fileName) throws IOException {
		return (FileUtil
				.find(fileName, new File(".").getCanonicalFile(), false)
				.getCanonicalFile());
	}

	private static final File find(String name, File current, boolean isdir)
			throws IOException {
		boolean has_git = false;
		for (File file : current.listFiles()) {
			if (file.getCanonicalPath().endsWith(File.separator + name)
					&& file.isDirectory() == isdir) {
				return (file);
				// Make sure that we don't go to far down...
			} else if (file.getCanonicalPath()
					.endsWith(File.separator + ".git")) {
				has_git = true;
			}
		} // FOR
			// If we didn't see an .svn directory, then we went too far down
		if (!has_git)
			throw new RuntimeException("Unable to find directory '" + name
					+ "' [last_dir=" + current.getAbsolutePath() + "]");
		File next = new File(current.getCanonicalPath() + File.separator + "..");
		return (FileUtil.find(name, next, isdir));
	}

	/**
	 * Normalizes the path to cater for Windows and other platforms
	 */
	public static String normalizePath(String path) {
		if (path == null) {
			return null;
		}
		if (SystemUtil.IS_WINDOWS) {
			// special handling for Windows where we need to convert / to \\
			return path.replace('/', '\\');
		} else {
			// for other systems make sure we use / as separators
			return path.replace('\\', '/');
		}
	}

	/**
	 * Returns a list of all the files in a directory whose name starts with the
	 * provided prefix
	 *
	 * @param dir
	 * @param filePrefix
	 * @return
	 * @throws IOException
	 */
	public static List<File> getFilesInDirectory(final File dir,
			final String filePrefix) throws IOException {
		assert (dir.isDirectory()) : "Invalid search directory path: " + dir;
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return (name.startsWith(filePrefix));
			}
		};
		return (Arrays.asList(dir.listFiles(filter)));
	}

	/**
	 * Compacts a path by stacking it and reducing <tt>..</tt>, and uses the
	 * given separator.
	 */
	public static String compactPath(String path, char separator) {
		if (path == null) {
			return null;
		}
		// only normalize if contains a path separator
		if (path.indexOf('/') == -1 && path.indexOf('\\') == -1) {
			return path;
		}
		// need to normalize path before compacting
		path = normalizePath(path);
		// preserve ending slash if given in input path
		boolean endsWithSlash = path.endsWith("/") || path.endsWith("\\");
		// preserve starting slash if given in input path
		boolean startsWithSlash = path.startsWith("/") || path.startsWith("\\");
		Stack<String> stack = new Stack<String>();
		// separator can either be windows or unix style
		String separatorRegex = "\\\\|/";
		String[] parts = path.split(separatorRegex);
		for (String part : parts) {
			if (part.equals("..") && !stack.isEmpty()
					&& !"..".equals(stack.peek())) {
				// only pop if there is a previous path, which is not a ".."
				// path either
				stack.pop();
			} else if (part.equals(".") || part.isEmpty()) {
				// do nothing because we don't want a path like foo/./bar or
				// foo//bar
			} else {
				stack.push(part);
			}
		}
		// build path based on stack
		StringBuilder sb = new StringBuilder();
		if (startsWithSlash) {
			sb.append(separator);
		}
		for (Iterator<String> it = stack.iterator(); it.hasNext();) {
			sb.append(it.next());
			if (it.hasNext()) {
				sb.append(separator);
			}
		}
		if (endsWithSlash && stack.size() > 0) {
			sb.append(separator);
		}
		return sb.toString();
	}

	// ========================================================================================================

	/**
	 * public static boolean copy(Set<File> sources, File toDir) throws
	 * IOException { boolean completeSuccess = true; int index = 0; for (File
	 * source : sources) { File target = new File(toDir, source.getName()); try
	 * { copy(source, target); } catch (IOException ioe) { completeSuccess =
	 * false; ioe.printStackTrace(); } index++; } return completeSuccess; }
	 */
	/**
	 * Copy the contents of is to os.
	 * 
	 * @param is
	 * @param os
	 * @param buf
	 *            Can be null
	 * @param close
	 *            If true, is is closed after the copy.
	 * @throws IOException
	 */
	/**
	 * public static final void copy(InputStream is, OutputStream os, byte[]
	 * buf, boolean close) throws IOException { int len; if (buf == null) { buf
	 * = new byte[4096]; } while ((len = is.read(buf)) > 0) { os.write(buf, 0,
	 * len); } os.flush(); if (close) { is.close(); } } public static void
	 * flush(byte[] data, File toFile) throws IOException { FileOutputStream fos
	 * = new FileOutputStream(toFile); fos.write(data); fos.flush();
	 * fos.close(); }
	 */
	/**
	 * Read <CODE>f</CODE> and return as byte[]
	 * 
	 * @param f
	 * @throws IOException
	 * @return bytes from <CODE>f</CODE>
	 */
	/**
	 * public static final byte[] load(File f) throws IOException {
	 * FileInputStream fis = new FileInputStream(f); return load(fis, true); }
	 */
	/**
	 * Copy the contents of is to the returned byte array.
	 * 
	 * @param is
	 * @param close
	 *            If true, is is closed after the copy.
	 * @throws IOException
	 */
	/**
	 * public static final byte[] load(InputStream is, boolean close) throws
	 * IOException { final ByteArrayOutputStream os = new
	 * ByteArrayOutputStream(); copy(is, os, null, close); return
	 * os.toByteArray(); }
	 */
	/**
	 * Class to compare Files by their embedded DateTimes.
	 */
	public static class FileNameDateTimeComparator implements Comparator<File> {
		private String pattern;
		private DateTimeZone zone;

		/**
		 * Creates a default instance where the pattern is "yyyy-MM-dd" and the
		 * default timezone of UTC.
		 */
		public FileNameDateTimeComparator() {
			this(null, null);
		}

		public FileNameDateTimeComparator(String pattern, DateTimeZone zone) {
			if (pattern == null) {
				this.pattern = "yyyy-MM-dd";
			} else {
				this.pattern = pattern;
			}
			if (zone == null) {
				this.zone = DateTimeZone.UTC;
			} else {
				this.zone = zone;
			}
		}

		public int compare(File f1, File f2) {
			// extract datetimes from both files
			DateTime dt1 = DateTimeUtil.parseEmbedded(f1.getName(), pattern,
					zone);
			DateTime dt2 = DateTimeUtil.parseEmbedded(f2.getName(), pattern,
					zone);
			// compare these two
			return dt1.compareTo(dt2);
		}
	}

	// ===========================================================================================

	public static void appendFile(File f, String content) throws Exception {
		BufferedWriter writer = null;
		try {
			writer = new BufferedWriter(new FileWriter(f, true));
			writer.write(content);
		} catch (Exception e) {
			throw e;
		} finally {
			if (writer != null) {
				try {
					writer.flush();
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void writeFile(File f, String content) throws Exception {
		BufferedWriter writer = null;
		try {
			writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(f), "utf-8"));
			writer.write(content);
		} catch (Exception e) {
			throw e;
		} finally {
			if (writer != null) {
				try {
					writer.flush();
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}


	public static List<String> readLine(File f) {
		List<String> result = new ArrayList<String>();
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(
					new FileInputStream(f), "utf-8"));
			String line = null;
			while ((line = reader.readLine()) != null)
				result.add(line);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	public static String getExt(File f) {
		return f.getName().substring(f.getName().lastIndexOf(".") + 1);
	}

	public static boolean deleteFolder(File folder) {
		return deleteFolderContents(folder) && folder.delete();
	}

	/**
	 * Delete folder's children files
	 *
	 * @param folder
	 * @return
	 */
	public static boolean deleteFolderContents(File folder) {
		File[] files = folder.listFiles();
		if (files != null) {
			for (File file : files) {
				if (file.isFile()) {
					if (!file.delete()) {
						return false;
					}
				} else {
					if (!deleteFolder(file)) {
						return false;
					}
				}
			}
		}
		return true;
	}

	public static BufferedImage getBufferedImage(String imagePath,
			boolean isRemote) throws Exception {
		return getBufferedImage(imagePath, isRemote, 0, 1 * 1000);
	}

	public static BufferedImage getBufferedImage(String imagePath)
			throws Exception {
		return getBufferedImage(imagePath, false, 0, 1 * 1000);
	}

	public static BufferedImage getBufferedImage(String imagePath,
			int retryTimes, long sleep) throws Exception {
		return getBufferedImage(imagePath, false, retryTimes, sleep);
	}

	public static BufferedImage getBufferedImage(String imagePath,
			boolean isRemote, int retryTimes, long sleep) throws Exception {
		if (imagePath == null || imagePath.trim().length() == 0)
			throw new Exception("image url can not be empty");
		int count = 0;
		while (true) {
			try {
				if (isRemote)
					return getRemote(imagePath);
				try {
					return getLocal(imagePath);
				} catch (Throwable e) {
					return getRemote(imagePath);
				}
			} catch (Throwable e) {
				if (count >= retryTimes) {
					throw new Exception(e);
				}
				Thread.sleep(sleep);
			}
			count++;
		}
	}

	private static BufferedImage getLocal(String imagePath) throws IOException {
		try {
			return toBufferedImage(Toolkit.getDefaultToolkit().getImage(
					imagePath));
		} catch (Throwable e) {
			return ImageIO.read(new File(imagePath));
		}
	}

	private static BufferedImage getRemote(String imagePath)
			throws MalformedURLException, IOException {
		URL url = new URL(imagePath.replace(" ", "%20"));
		try {
			return toBufferedImage(Toolkit.getDefaultToolkit().getImage(url));
		} catch (Throwable e1) {
			return ImageIO.read(url);
		}
	}

	public static BufferedImage toBufferedImage(Image image) {
		if (image instanceof BufferedImage) {
			return (BufferedImage) image;
		}
		// This code ensures that all the pixels in the image are loaded
		image = new ImageIcon(image).getImage();
		// Determine if the image has transparent pixels; for this method's
		// implementation, see e661 Determining If an Image Has Transparent
		// Pixels
		// boolean hasAlpha = hasAlpha(image);
		// Create a buffered image with a format that's compatible with the
		// screen
		BufferedImage bimage = null;
		GraphicsEnvironment ge = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		try {
			// Determine the type of transparency of the new buffered image
			int transparency = Transparency.OPAQUE;
			/*
			 * if (hasAlpha) { transparency = Transparency.BITMASK; }
			 */
			// Create the buffered image
			GraphicsDevice gs = ge.getDefaultScreenDevice();
			GraphicsConfiguration gc = gs.getDefaultConfiguration();
			bimage = gc.createCompatibleImage(image.getWidth(null),
					image.getHeight(null), transparency);
		} catch (HeadlessException e) {
			// The system does not have a screen
		}
		if (bimage == null) {
			// Create a buffered image using the default color model
			int type = BufferedImage.TYPE_INT_RGB;
			// int type = BufferedImage.TYPE_3BYTE_BGR;//by wang
			/*
			 * if (hasAlpha) { type = BufferedImage.TYPE_INT_ARGB; }
			 */
			bimage = new BufferedImage(image.getWidth(null),
					image.getHeight(null), type);
		}
		// Copy image to buffered image
		Graphics g = bimage.createGraphics();
		// Paint the image onto the buffered image
		g.drawImage(image, 0, 0, null);
		g.dispose();
		return bimage;
	}

	public static boolean exists(String filePath) {
		File dir = new File(CommonUtil.uriDecoding(filePath));
		return dir.exists();
	}

	public static File[] getFiles(String str) {
		File dir = new File(CommonUtil.uriDecoding(str));
		File[] result = null;
		if (dir.isDirectory()) {
			result = dir.listFiles();
		}
		return result;
	}

	public static String getTopClassPath(Class<?> clazz) {
		String path = CommonUtil.uriDecoding(clazz.getResource("/").getPath());
		return path;
	}

	public static void main(String[] args) throws Exception {
		String c = FileUtil.readFile(new File("d:/seasky.lrc"));
		System.out.println(c);
	}

	/**
	 * get the jars path
	 *
	 * @return
	 */
	public static String getLib() {
		return CommonUtil.uriDecoding(FileUtil.getParent(
				FileUtil.getTopClassPath(FileUtil.class), 1)
				+ "lib");
	}

	public static String[] getChildrenPath(File parent) {
		File[] files = parent.listFiles();
		String[] result = new String[files.length];
		for (int i = 0; i < files.length; i++)
			result[i] = CommonUtil.uriDecoding(files[i].getAbsolutePath());
		return result;
	}

	public static List<String> getJars() {
		Collection<String> jars = new HashSet<String>();
		Enumeration<URL> urls;
		try {
			urls = FileUtil.class.getClassLoader().getResources(
					"META-INF/MANIFEST.MF");
			while (urls.hasMoreElements()) {
				URL url = (URL) urls.nextElement();
				String path = url.getFile().replace("file:/", "")
						.replace("!/META-INF/MANIFEST.MF", "");
				jars.add(CommonUtil.uriDecoding(path));
			}
			File jarDir = new File(getLib());
			if (jarDir.isDirectory() && jarDir.exists()) {
				for (File jar : jarDir.listFiles()) {
					jars.add(CommonUtil.uriDecoding(jar.getAbsolutePath()));
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return new ArrayList<String>(jars);
	}

	public static String getClassPath(String folderName) {
		return getParent(getTopClassPath(FileUtil.class), 1) + folderName;
	}

	public static String getCurrPath(Class<?> clazz) {
		return CommonUtil.uriDecoding(clazz.getResource("/").getPath()
				+ clazz.getName().replace(".", File.separator));
	}

	public static boolean createDir(String path) {
		boolean flag = false;
		File file = new File(CommonUtil.uriDecoding(path));
		if (!file.exists()) {
			if (!file.isDirectory()) {
				flag = file.mkdir();
			}
		}
		return flag;
	}

	public static boolean createFile(String path) throws IOException {
		return createFile(path, false);
	}

	public static boolean createFile(File file, boolean isDelete)
			throws IOException {
		boolean flag = true;
		if (file.exists()) {
			if (isDelete) {
				file.delete();
				file.createNewFile();
			} else {
				flag = false;
			}
		} else {
			file.createNewFile();
		}
		return flag;
	}

	public static boolean createFile(String path, boolean isDelete)
			throws IOException {
		File file = new File(CommonUtil.uriDecoding(path));
		return createFile(file, isDelete);
	}

	public static boolean moveFileTo(File oldFile, String newDir) {
		StringBuilder sb = new StringBuilder(newDir);
		sb.append(File.separator).append(oldFile.getName());
		File toDir = new File(CommonUtil.uriDecoding(sb.toString()));
		boolean flag = false;
		if (!toDir.exists()) {
			flag = oldFile.renameTo(toDir);
		}
		return flag;
	}

	public static String getParent(File f, int floor) {
		String result = "";
		if (f != null && f.exists()) {
			for (int i = 0; i < floor; ++i) {
				f = f.getParentFile();
			}
			if (f != null && f.exists()) {
				result = f.getPath();
			}
		}
		return CommonUtil.uriDecoding(result) + File.separator;
	}

	public static String getParent(String path, int floor) {
		return getParent(new File(path), floor);
	}

	public static boolean deleteFile(File file) {
		boolean flag = false;
		if (file != null && file.exists()) {
			if (file.isDirectory()) {
				for (File f : file.listFiles()) {
					deleteFile(f);
				}
			}
			flag = file.delete();
		}
		return flag;
	}

	public static boolean isValidFileName(String fileName) {
		if (fileName == null || fileName.length() > 255)
			return false;
		else {
			return fileName.matches("[^\\s\\\\/:\\*\\?\\\"<>\\|](\\x20|[^\\s\\\\/:\\*\\?\\\"<>\\|])*[^\\s\\\\/:\\*\\?\\\"<>\\|\\.]$");
		}
	}


}
